namespace ScrumSprintMonitor.Implementation
{
	#region #using Directives

	using System;
	using System.ComponentModel;
	using System.Diagnostics;

	using ContinuousLinq.Reactive;

	using Infrastructure;
	using Infrastructure.Configuration;

	using Microsoft.Practices.ServiceLocation;

	#endregion

	public class ReportableData : ReactiveObject,
	                              IReportableData,
	                              IDisposable
	{
		private readonly IServiceLocator serviceLocator;
		private readonly ISprintConfiguration sprintConfiguration;
		private IConnection connection;
		private ISprint currentSprint;
		private bool disposed;
		private IBuildInfo lastBuild;

		static ReportableData()
		{
			var dependsOn = Register<ReportableData>();

			dependsOn.Call(me =>
			               {
			               	me.RetrieveLastBuild();
			               	me.RetrieveCurrentSprint();
			               }).OnChanged(me => me.Connection.IsConnected);
		}

		public ReportableData(IServiceLocator serviceLocator, IConnection connection, ISprintConfiguration sprintConfiguration)
		{
			this.serviceLocator = serviceLocator;
			this.sprintConfiguration = sprintConfiguration;

			var notifySprintPropertyChanged = sprintConfiguration as INotifyPropertyChanged;
			if (notifySprintPropertyChanged != null)
			{
				notifySprintPropertyChanged.PropertyChanged += OnNotifySprintConfigPropertyChanged;
			}

			Connection = connection;
		}

		#region IReportableData Members

		public IConnection Connection
		{
			[DebuggerStepThrough]
			get
			{
				GuardDisposed();

				return this.connection;
			}
			private set
			{
				GuardDisposed();

				if (value != this.connection)
				{
					this.connection = value;

					OnPropertyChanged("Connection");
				}
			}
		}

		public IBuildInfo LastBuild
		{
			[DebuggerStepThrough]
			get
			{
				GuardDisposed();

				return this.lastBuild;
			}
			private set
			{
				GuardDisposed();

				if (value != this.lastBuild)
				{
					if (this.lastBuild as IDisposable != null)
					{
						((IDisposable) this.lastBuild).Dispose();
						this.lastBuild = null;
					}

					this.lastBuild = value;

					OnPropertyChanged("LastBuild");
				}
			}
		}

		public ISprint CurrentSprint
		{
			[DebuggerStepThrough]
			get
			{
				GuardDisposed();

				return this.currentSprint;
			}
			private set
			{
				GuardDisposed();

				if (value != this.currentSprint)
				{
					if (this.currentSprint as IDisposable != null)
					{
						((IDisposable) this.currentSprint).Dispose();
						this.currentSprint = null;
					}

					this.currentSprint = value;

					OnPropertyChanged("CurrentSprint");
				}
			}
		}

		#endregion

		#region Implementation of IDisposable

		public void Dispose()
		{
			SuppressPropertyChanged = true;

			LastBuild = null;
			CurrentSprint = null;

			this.disposed = true;
		}

		#endregion

		[DebuggerStepThrough]
		private void GuardDisposed()
		{
			if (this.disposed)
			{
				throw new ObjectDisposedException("ReportableData");
			}
		}

		private void RetrieveCurrentSprint()
		{
			if (!this.disposed)
			{
				CurrentSprint = (this.connection.IsDisposed ? null : this.serviceLocator.GetInstance<ISprint>());
			}
		}

		private void RetrieveLastBuild()
		{
			if (!this.disposed)
			{
				LastBuild = (this.connection.IsDisposed ? null : this.connection.GetBuildInfo(this.sprintConfiguration));
			}
		}

		private void OnNotifySprintConfigPropertyChanged(object sender, PropertyChangedEventArgs e)
		{
			GuardDisposed();

			RetrieveLastBuild();
			RetrieveCurrentSprint();
		}
	}
}